package com.hjy.im.client;

import com.hjy.im.message.*;
import com.hjy.im.protocol.MessageSharableCodec;
import com.hjy.im.protocol.ProcotolFrameDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public class ChatClient {
    static final Logger log = LoggerFactory.getLogger(ChatClient.class);

    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();
        LoggingHandler loggingHandler = new LoggingHandler(LogLevel.DEBUG);
        MessageSharableCodec messageSharableCodec = new MessageSharableCodec();

        //倒计时锁，主次线程之间通信
        CountDownLatch WAIT_FOR_LOGIN = new CountDownLatch(1);
        //登录状态初始值 主次线程之间共享
        AtomicBoolean LOGIN = new AtomicBoolean(false);
        AtomicBoolean EXIT = new AtomicBoolean(false);

        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                         ch.pipeline().addLast(new ProcotolFrameDecoder());
                         //需要接收所有的类型
//                         ch.pipeline().addLast(loggingHandler);
                         ch.pipeline().addLast(messageSharableCodec);
                        // 用来判断 是不是读 空闲时间过长，或写空闲时间过长 (读，写，读写空闲时间限制) 0表示不关心
                         ch.pipeline().addLast(new IdleStateHandler(0, 10, 0));
                         /*
                        ################################################################
                        ###### ChannelDuplexHandler 可以同时作为 入站 和 出站处理器  #######
                        #####          没读到数据 触发   IdleState.READER_IDLE     #######
                        #####  10 秒内  写      触发   IdleState.WRITER_IDLE       #######  【写要比服务端读的频率高些】
                        #####       读写     触发   IdleState.ALL_IDLE            #######
                        ################################################################
                         */

                         ch.pipeline().addLast(new ChannelDuplexHandler(){
                             @Override
                             public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                 IdleStateEvent event = (IdleStateEvent) evt;
                                 //是否读超时
                                 if(event.state() == IdleState.WRITER_IDLE){
                                     log.debug("10 秒没写数据了，发送心跳包===");
                                     ctx.writeAndFlush(new PingMessage());
                                 }
                             }
                         });
                        //创建入栈处理器，写入数据后就会触发出站操作
                        //1.登录
                        //2 另起线程，菜单选择收发消息操作
                        ch.pipeline().addLast("chat client handler", new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                //1。处理登录
                                if (msg instanceof LoginResponseMessage){
                                    LoginResponseMessage responseMessage = (LoginResponseMessage) msg;
                                    if(responseMessage.isSuccess()) LOGIN.set(true);
                                }
                                WAIT_FOR_LOGIN.countDown(); //减1 唤醒线程 System.in
                            }

                            //第一步
                            @Override //连接建立之后触发一次
                            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                //另起线程，不然主线程会阻塞 接收用户输入的操作
                                new Thread(()->{
                                    final Scanner scanner = new Scanner(System.in);
                                    System.out.println("输入用户名：");
                                    final String username = scanner.nextLine();
                                    System.out.println("输密码：");
                                    final String password = scanner.nextLine();
                                    //够着消息对象
                                    LoginRequestMessage message = new LoginRequestMessage(username, password);
                                    ctx.writeAndFlush(message);

                                    //第二步
                                    log.debug("等待后续操作");
                                    try{
                                        WAIT_FOR_LOGIN.await();;//柱塞，等ChannelRead 回来之后操作

                                    }catch (InterruptedException e){
                                        e.printStackTrace();
                                    }
                                    //第四步
                                    //登录失败停止运行
                                    if(!LOGIN.get()){
                                        ctx.channel().close();//触发关闭触发 【channel.closeFuture().sync(); 向下运行】
                                        return;
                                    }

                                    //打印菜单
                                    // 打印菜单
                                    while (true) {
                                        System.out.println("============ 功能菜单 ============");
                                        System.out.println("send [username] [content]");
                                        System.out.println("gsend [group name] [content]");
                                        System.out.println("gcreate [group name] [m1,m2,m3...]");
                                        System.out.println("gmembers [group name]");
                                        System.out.println("gjoin [group name]");
                                        System.out.println("gquit [group name]");
                                        System.out.println("quit");
                                        System.out.println("==================================");

                                        String command = scanner.nextLine();
                                        final String[] s = command.split(" ");
                                        switch (s[0]) {
                                            case "send": // 发送消息
                                                ctx.writeAndFlush(new ChatRequestMessage(username, s[1], s[2]));
                                                break;
                                            case "gsend": // 群里 发送消息
                                                ctx.writeAndFlush(new GroupChatRequestMessage(username, s[1], s[2]));
                                                break;
                                            case "gcreate": // 创建群
                                                final Set<String> set = new HashSet(Arrays.asList(s[2].split(",")));
                                                set.add(username);
                                                ctx.writeAndFlush(new GroupCreateRequestMessage(s[1], set));
                                                break;
                                            case "gmembers": // 查看群列表
                                                ctx.writeAndFlush(new GroupMembersRequestMessage(s[1]));
                                                break;
                                            case "gjoin":
                                                ctx.writeAndFlush(new GroupJoinRequestMessage(username, s[1]));
                                                break;
                                            case "gquit":
                                                ctx.writeAndFlush(new GroupQuitRequestMessage(username, s[1]));
                                                break;
                                            case "quit":
                                                ctx.channel().close(); // 触发 【channel.closeFuture().sync(); 向下运行】
                                                return;
                                        }
                                    }
                                },"system in").start();
                            }

                            // 在连接断开时触发
                            @Override
                            public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                                log.debug("Client...主动-连接已经断开，按任意键退出..");
                                EXIT.set(true);
                            }

                            // 在出现异常时触发
                            @Override
                            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                log.debug("Client...异常-连接已经断开，按任意键退出..{}", cause.getMessage());
                                EXIT.set(true);
                            }
                        });
                }
            });
            Channel channel = bootstrap.connect("localhost",9090).sync().channel();
            channel.closeFuture().sync();


        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }

    }
}
